﻿using mozhi.smarterp.Finance.Accounts;
using mozhi.smarterp.Finance.Accounts.Repository;
using mozhi.smarterp.Finance.Accounts.Services;
using mozhi.smarterp.Finance.Basedatas.Repository;
using mozhi.smarterp.Finance.Basedatas.Services;
using mozhi.smarterp.Finance.Enums;
using mozhi.smarterp.Finance.Finances;
using mozhi.smarterp.Finance.Finances.Repository;
using mozhi.smarterp.Finance.Querys;
using Mozhi.Abp.Core.Extensions;
using Mozhi.Abp.Domain.BasicData.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace mozhi.smarterp.Finance.Reports
{
    public class SubsidiaryledgerReportAppService : ReportAppService, ISubsidiaryledgerReportAppService
    {
        private IJournalRepository _journalRepository;
        private AccountManager _accountManager;
        private IJournalNumberPrefixRepository _journalNumberPrefixRepository;

        public SubsidiaryledgerReportAppService(IAccountRepository accountRepository, IDimensionGroupValueRepository dimensionGroupValueRepository,
            ICurrencyRepository currencyRepository, ISettlementRepository settlementRepository, IAccountBalanceRepository accountBalanceRepository,
            AccountBalanceManager accountBalanceManager, DimensionGroupValueManager dimensionGroupValueManager, IJournalRepository journalRepository,
            IOrganizationRepository organizationRepository, AccountManager accountManager , IJournalNumberPrefixRepository journalNumberPrefixeRepository)
            : base(accountRepository, dimensionGroupValueRepository, currencyRepository, settlementRepository,
                  accountBalanceRepository, accountBalanceManager, dimensionGroupValueManager, organizationRepository)
        {
            _journalRepository = journalRepository;
            _accountManager = accountManager;
            _journalNumberPrefixRepository = journalNumberPrefixeRepository;
        }

        public async Task<List<SubsidiaryledgerReportDto>> GetData(ReportQueryBase query)
        {
             var result = new List<SubsidiaryledgerReportDto>();

            var balances = await GetAccountBalancesAsync(query);

            if(balances==null || balances.Count==0)
                return result;

            var balanceReports = await ConvertToBalanceReportDto(balances);

            balanceReports = MergeBalances(query, balanceReports);

            var tuples = await GetJournalsAsync(query);

            var accounts = tuples.Item1;
            var journals = tuples.Item2;
            var journalNumberPrefixs = tuples.Item3;

            //先按照期间
            var groupsByPeriod = balanceReports.GroupBy(x => x.Period);

            foreach (IGrouping<int, AccountBalanceReportDto> info in groupsByPeriod)
            {
                var balancesForPeriod = info.ToList();

                //1条余额需要转换为四条分类账余额    
                foreach (var balance in balancesForPeriod)
                {
                    var accountId = balance.AccountId;

                    var period = balance.Period;

                    var minPeriod = balances.Where(x => x.AccountId == balance.AccountId).Min(x => x.Period);

                    //如果是核算维度查询，凭证过滤一下，放到对应的核算维度余额下面
                    var matchJournals = journals.Where(x => x.Date.ToPeriod() == period).ToList();
                    var matchJournalItems = matchJournals.Select(x => x.Items).ToList().Join().WhereIf(query.IncludeDimension == true, x => x.DimensionGroupValueId == balance.DimensionGroupValueId).ToList();

                    if (query.AccountIds != null && query.AccountIds.Count > 0)
                    {
                        //有多个科目查询时，凭证包括了所有查询科目的凭证，所有要再过滤一次
                        var childrenAccountIds = _accountManager.GetChildAccounts(accountId, accounts).Select(x => x.Id);

                        matchJournalItems = matchJournalItems.Where(x => x.AccountId == accountId || childrenAccountIds.Contains(x.AccountId)).ToList();
                    }

                    matchJournals.ForEach(journal =>
                    {
                        journal.ChangeItems(matchJournalItems.Where(x => x.JournalId == journal.Id).ToList());
                    });

                    matchJournals = matchJournals.Where(x => x.Items.Count > 0).ToList();

                    bool isFirstPeriod = !result.Exists(x => x.AccountId == balance.AccountId && x.DimensionGroupValueId == balance.DimensionGroupValueId && x.Type == 0);
                    bool isBaseCurrency = !query.CurrencyId.HasValue;

                    var subsidiaryLedgers = GetAccountSubsidiaryLedgers(balance, matchJournals, isFirstPeriod, isBaseCurrency, journalNumberPrefixs);

                    if (subsidiaryLedgers != null)
                        result.AddRange(subsidiaryLedgers);
                }
            }


            return result;
        }

        #region
        private async Task<Tuple<List<Account>,List<Journal> , List<JournalNumberPrefix>>> GetJournalsAsync(ReportQueryBase query)
        {
            //var accountIds = new List<Guid>();

            //List<Account> accounts = new List<Account>();

            //if (query.AccountId.HasValue)
            //{
            //    accounts = await _accountRepository.GetAccountChildrenAsync(query.AccountId.Value);

            //    accountIds = accounts.Where(x => x.IsLeaf == true).Select(x => x.Id).ToList();
            //}
            //else if (query.AccountIds != null && query.AccountIds.Count > 0)
            //{
            //    foreach (var accountId in query.AccountIds)
            //    {
            //        var chilrenAccounts = await _accountRepository.GetAccountChildrenAsync(query.AccountId.Value);

            //        var tempAccountIds = chilrenAccounts.Where(x => x.IsLeaf == true).Select(x => x.Id).ToList();

            //        accountIds.AddRange(tempAccountIds);

            //        accounts.AddRange(chilrenAccounts);
            //    }
            //}

            //accountIds = accountIds.Distinct().ToList();

            //var journals = await _journalRepository.FindAsync(new JournalPageQuery() { PageIndex = 1, PageSize = int.MaxValue, AccountIds = accountIds, IncludeDetail = true });

            //var numberPrefixIds = journals.Select(x=>x.NumberPrefixId).Distinct().ToList();

            //var numberPrefixs = await _journalNumberPrefixRepository.FindAsync(new PageQuery() { Ids = numberPrefixIds, PageIndex = 1, PageSize = int.MaxValue });

            //return new Tuple<List<Account>, List<Journal>, List<JournalNumberPrefix>>(accounts, journals, numberPrefixs);
            throw new NotImplementedException();
        }

        private List<AccountBalanceReportDto> MergeBalances(ReportQueryBase query , List<AccountBalanceReportDto> balances)
        {
            if (query.IncludeDimension == true && !query.CurrencyId.HasValue)
            {
                //如果显示附加属性，显示的又是综合本位币（因为带核算维度的余额都带币别）
                balances = (from a in balances
                            group a by new { a.Period, a.AccountId, a.DimensionGroupValueId } into b
                        select new AccountBalanceReportDto()
                        {
                            AccountId = b.Key.AccountId,
                            AccountCode = b.First().AccountCode,
                            Direction = b.First().Direction,
                            AccountName = b.First().AccountName,
                            IsLeaf = b.First().IsLeaf,
                            Period = b.Key.Period,
                            DimensionGroupValueId = b.Key.DimensionGroupValueId,
                            DimensionGroupValueItems = b.First().DimensionGroupValueItems,
                            OpeningAmount = b.Where(x => x.OpeningAmount.HasValue).Sum(x => x.OpeningAmount),
                            OpeningAmountFr = b.Where(x => x.OpeningAmount.HasValue).Sum(x => x.OpeningAmount),
                            DebitAmount = b.Where(x => x.DebitAmount.HasValue).Sum(x => x.DebitAmount),
                            DebitAmountFr = b.Where(x => x.DebitAmount.HasValue).Sum(x => x.DebitAmount),
                            CreditAmount = b.Where(x => x.CreditAmount.HasValue).Sum(x => x.CreditAmount),
                            CreditAmountFr = b.Where(x => x.CreditAmount.HasValue).Sum(x => x.CreditAmount),
                            CurrentYearDebitAmount = b.Where(x => x.CurrentYearDebitAmount.HasValue).Sum(x => x.CurrentYearDebitAmount),
                            CurrentYearDebitAmountFr = b.Where(x => x.CurrentYearDebitAmountFr.HasValue).Sum(x => x.CurrentYearDebitAmountFr),
                            CurrentYearCreditAmount = b.Where(x => x.CurrentYearCreditAmount.HasValue).Sum(x => x.CurrentYearCreditAmount),
                            CurrentYearCreditAmountFr = b.Where(x => x.CurrentYearCreditAmountFr.HasValue).Sum(x => x.CurrentYearCreditAmountFr),
                            ClosingAmount = b.Where(x => x.ClosingAmount.HasValue).Sum(x => x.ClosingAmount),
                            ClosingAmountFr = b.Where(x => x.ClosingAmount.HasValue).Sum(x => x.ClosingAmount),
                        }).ToList();
            }

            return balances;
        }


        /// <summary>
        /// 转换为分类账数据
        /// </summary>
        /// <param name="balance"></param>
        /// <param name="isFirstPeriod"></param>
        /// <param name="isBaseCurrency">是否综合本位币</param>
        /// <returns></returns>
        private List<SubsidiaryledgerReportDto> GetAccountSubsidiaryLedgers(AccountBalanceReportDto balance, List<Journal> journals,
            bool isFirstPeriod, bool isBaseCurrency,  List<JournalNumberPrefix> numberPrefixes)
        {
            var result = new List<SubsidiaryledgerReportDto>();

            if (balance == null)
                return result;

            for (var i = 0; i < 4; i++)
            {
                //跳过期初余额行
                if (!isFirstPeriod && i == 0)
                    continue;

                var subsidiaryLedger = new SubsidiaryledgerReportDto();

                subsidiaryLedger.AccountId = balance.AccountId;
                subsidiaryLedger.AccountCode = balance.AccountCode;
                subsidiaryLedger.AccountName = balance.AccountName;


                if (balance.DimensionGroupValueId != null && balance.DimensionGroupValueItems != null)
                    subsidiaryLedger.AccountName = ReloadAccountName(subsidiaryLedger.AccountName, balance.DimensionGroupValueItems);

                subsidiaryLedger.IsLeaf = balance.IsLeaf;
                subsidiaryLedger.DimensionGroupValueId = balance.DimensionGroupValueId;
                subsidiaryLedger.DimensionGroupValueItems = balance.DimensionGroupValueItems;


                subsidiaryLedger.Summary = GetSummary(i);
                subsidiaryLedger.Type = i;
                subsidiaryLedger.Direction = balance.Direction;
                subsidiaryLedger.BalanceDirection = GetBalanceDirection((int)balance.Direction);
                if (i == 0)
                {
                    subsidiaryLedger.Date = balance.Period.ToFirstDayDateTime();
                    subsidiaryLedger.DebitAmount = balance.Direction == AccountDirectionType.Debit ? balance.OpeningAmount : null;
                    subsidiaryLedger.DebitAmountFr = balance.Direction == AccountDirectionType.Debit ? balance.OpeningAmountFr : null;
                    subsidiaryLedger.CreditAmount = balance.Direction == AccountDirectionType.Credit ? balance.OpeningAmount : null;
                    subsidiaryLedger.CreditAmountFr = balance.Direction == AccountDirectionType.Credit ? balance.OpeningAmountFr : null;

                    subsidiaryLedger.Balance = balance.OpeningAmount.HasValue ? balance.OpeningAmount : 0;
                    subsidiaryLedger.BalanceFr = balance.OpeningAmountFr.HasValue ? balance.OpeningAmountFr : 0;

                    //如果综合本位币，余额未0就可以，如果选了币别，需要两个都为0
                    subsidiaryLedger.BalanceDirection = isBaseCurrency ?
                        subsidiaryLedger.Balance == 0 ? GetBalanceDirection(0) : GetBalanceDirection((int)subsidiaryLedger.Direction) :
                        subsidiaryLedger.Balance == 0 && subsidiaryLedger.BalanceFr == 0 ? GetBalanceDirection(0) : GetBalanceDirection((int)subsidiaryLedger.Direction);

                    result.Add(subsidiaryLedger);
                }
                else if (i == 1)
                {
                    journals = journals ?? new List<Journal>();
                    journals = journals.OrderBy(x => x.Date).ThenBy(x => x.NumberPrefixId).ThenBy(x => x.Number).ToList();

                    var openingBalance = balance.OpeningAmount.HasValue ? balance.OpeningAmount.Value : 0;
                    var openingBalanceFr = balance.OpeningAmountFr.HasValue ? balance.OpeningAmountFr.Value : 0;

                    foreach (var journal in journals)
                    {
                        foreach (var journalItem in journal.Items)
                        {
                            var tempSubsidiaryLedger = new SubsidiaryledgerReportDto();

                            tempSubsidiaryLedger.DimensionGroupValueId = balance.DimensionGroupValueId;
                            tempSubsidiaryLedger.DimensionGroupValueItems = balance.DimensionGroupValueItems;
                            tempSubsidiaryLedger.AccountId = balance.AccountId;
                            tempSubsidiaryLedger.AccountCode = balance.AccountCode;
                            tempSubsidiaryLedger.AccountName = balance.AccountName;
                            if (balance.DimensionGroupValueId != null && balance.DimensionGroupValueItems != null)
                                tempSubsidiaryLedger.AccountName = ReloadAccountName(subsidiaryLedger.AccountName, balance.DimensionGroupValueItems);

                            tempSubsidiaryLedger.Summary = journalItem.Summary;
                            tempSubsidiaryLedger.Date = journal.Date;
                            tempSubsidiaryLedger.Number = $"{numberPrefixes.FirstOrDefault(x => x.Id == journal.NumberPrefixId)?.Name}-{journal.Number}";
                            tempSubsidiaryLedger.JournalId = journal.Id;


                            tempSubsidiaryLedger.BalanceDirection = GetBalanceDirection(0);
                            tempSubsidiaryLedger.Type = 2;

                            if (journalItem.JournalDirection == AccountDirectionType.Debit)
                            {
                                tempSubsidiaryLedger.DebitAmount = journalItem.DebitAmount;
                                tempSubsidiaryLedger.DebitAmountFr = (decimal?)journalItem.AmountFr;
                            }
                            else
                            {
                                tempSubsidiaryLedger.CreditAmount = journalItem.CreditAmount;
                                tempSubsidiaryLedger.CreditAmountFr = (decimal?)journalItem.AmountFr;
                            }


                            var debitAmount = tempSubsidiaryLedger.DebitAmount.HasValue ? tempSubsidiaryLedger.DebitAmount.Value : 0;
                            var debitAmountFr = tempSubsidiaryLedger.DebitAmountFr.HasValue ? tempSubsidiaryLedger.DebitAmountFr.Value : 0;
                            var creditAmount = tempSubsidiaryLedger.CreditAmount.HasValue ? tempSubsidiaryLedger.CreditAmount.Value : 0;
                            var creditAmountFr = tempSubsidiaryLedger.CreditAmountFr.HasValue ? tempSubsidiaryLedger.CreditAmountFr.Value : 0;

                            //计算余额
                            openingBalance = balance.Direction == AccountDirectionType.Debit ? openingBalance + debitAmount - creditAmount
                                : openingBalance - debitAmount + creditAmount;

                            openingBalanceFr = balance.Direction == AccountDirectionType.Debit ? openingBalanceFr + debitAmountFr - creditAmountFr
                                : openingBalanceFr - debitAmountFr + creditAmountFr;


                            tempSubsidiaryLedger.Balance = openingBalance;
                            tempSubsidiaryLedger.BalanceFr = openingBalanceFr;

                            //如果综合本位币，余额未0就可以，如果选了币别，需要两个都为0
                            tempSubsidiaryLedger.BalanceDirection = isBaseCurrency ?
                                tempSubsidiaryLedger.Balance == 0 ? GetBalanceDirection(0) : GetBalanceDirection((int)tempSubsidiaryLedger.Direction) :
                                tempSubsidiaryLedger.Balance == 0 && tempSubsidiaryLedger.BalanceFr == 0 ? GetBalanceDirection(0) : GetBalanceDirection((int)tempSubsidiaryLedger.Direction);

                            result.Add(tempSubsidiaryLedger);
                        }

                    }

                }
                else if (i == 2)
                {
                    subsidiaryLedger.Date = balance.Period.ToLastDayDateTime();

                    //需要加上未审核凭证的金额
                    subsidiaryLedger.DebitAmount = balance.DebitAmount;
                    subsidiaryLedger.DebitAmountFr = balance.DebitAmountFr;
                    subsidiaryLedger.CreditAmount = balance.CreditAmount;
                    subsidiaryLedger.CreditAmountFr = balance.CreditAmountFr;
                    subsidiaryLedger.Direction = balance.Direction;

                    subsidiaryLedger.Balance = balance.ClosingAmount.HasValue ? balance.ClosingAmount : 0;
                    subsidiaryLedger.BalanceFr = balance.ClosingAmountFr.HasValue ? balance.ClosingAmountFr : 0;

                    //如果综合本位币，余额未0就可以，如果选了币别，需要两个都为0
                    subsidiaryLedger.BalanceDirection = isBaseCurrency ?
                                 subsidiaryLedger.Balance == 0 ? GetBalanceDirection(0) : GetBalanceDirection((int)subsidiaryLedger.Direction) :
                                 subsidiaryLedger.Balance == 0 && subsidiaryLedger.BalanceFr == 0 ? GetBalanceDirection(0) : GetBalanceDirection((int)subsidiaryLedger.Direction);

                    result.Add(subsidiaryLedger);
                }
                else if (i == 3)
                {
                    subsidiaryLedger.Date = balance.Period.ToLastDayDateTime();

                    subsidiaryLedger.DebitAmount = balance.CurrentYearDebitAmount;
                    subsidiaryLedger.DebitAmountFr = balance.CurrentYearDebitAmountFr;
                    subsidiaryLedger.CreditAmount = balance.CurrentYearCreditAmount;
                    subsidiaryLedger.CreditAmountFr = balance.CurrentYearCreditAmountFr;

                    subsidiaryLedger.Balance = balance.ClosingAmount.HasValue ? balance.ClosingAmount : 0;
                    subsidiaryLedger.BalanceFr = balance.ClosingAmountFr.HasValue ? balance.ClosingAmountFr : 0;

                    //如果综合本位币，余额未0就可以，如果选了币别，需要两个都为0
                    subsidiaryLedger.BalanceDirection = isBaseCurrency ?
                                 subsidiaryLedger.Balance == 0 ? GetBalanceDirection(0) : GetBalanceDirection((int)subsidiaryLedger.Direction) :
                                 subsidiaryLedger.Balance == 0 && subsidiaryLedger.BalanceFr == 0 ? GetBalanceDirection(0) : GetBalanceDirection((int)subsidiaryLedger.Direction);

                    result.Add(subsidiaryLedger);
                }
            }

            return result;
        }

       

        #endregion
    }



}
